import org.jbox2d.common.Vec2;
import pulpcore.animation.Fixed;
import pulpcore.image.Colors;
import pulpcore.image.CoreFont;
import pulpcore.math.CoreMath;
import pulpcore.sound.Sound;
import pulpcore.sprite.*;
import pulpfizz.physics.BodyUtils;
import pulpfizz.physics.Material;
import pulpfizz.pulp.body.BodySprite;
import pulpfizz.pulp.body.PhysicsLayer;

import java.util.ArrayList;

/**
 * Created by will
 * Date: May 8, 2008 1:41:25 AM
 */
public class Arena extends PhysicsLayer {
    Vec2 shadow = new Vec2(20,20);
    
    static Sound ding = Sound.load("shipbell.ogg");
    static CoreFont font = CoreFont.load("digital.font.png");

    int scorePlayer = 0;
    int scoreComputer = 0;
    Label scoreHome, scoreAway, scoreHomeVal, scoreAwayVal;
    ArrayList<Shot> shots = new ArrayList<Shot>();
    ArrayList<Target> targets = new ArrayList<Target>();
    ArrayList<Creature> creatures = new ArrayList<Creature>();
    private ImageSprite woodTable;

    public Group scores = new Group();

    public Arena(double scaling, double x, double y, double width, double height) {
        super(scaling, x, y, width, height);
        int labelY = 50 + font.getHeight();
        scoreHome = new Label(font,"PLAYER", 50, labelY);
        scoreHome.setAnchor(Sprite.SOUTH_WEST);
        scoreAway = new Label(font,"CPU", 700, labelY);
        scoreAway.setAnchor(Sprite.SOUTH_EAST);
        scoreHomeVal = new Label(font,"0",10+font.getStringWidth("PLAYER"), labelY);
        scoreAwayVal = new Label(font,"0",760,labelY);
        scoreHomeVal.setAnchor(Sprite.SOUTH_WEST);
        scoreAwayVal.setAnchor(Sprite.SOUTH_EAST);
        
        woodTable = new ImageSprite("woodtable.JPG",40,40);
        add(woodTable);
        scores.add(scoreHome);
        scores.add(scoreAway);
        scores.add(scoreHomeVal);
        scores.add(scoreAwayVal);
        add(scores);
    }

    public Arena(double scaling) {
        this(scaling,0,0,100,100);
    }

    // Compute nearest target for Computer AI shots
    public Vec2 getNearestTarget(Vec2 baseVec) {
        Vec2 bestVec = null;
        for (Target target: targets) {
            Vec2 shotVec = new Vec2((float)target.x.get(),(float)target.y.get());
            shotVec.subLocal(baseVec);
            if(bestVec == null || shotVec.length() < bestVec.length()) {
                bestVec = shotVec;
            }
        }
        for (Creature creature: creatures) {
            for(int i=0;i<creature.getNumSprites();i++) {
                Vec2 shotVec = new Vec2((float)creature.get(i).x.get(),(float)creature.get(i).y.get());
                shotVec.subLocal(baseVec);
                if(bestVec == null || shotVec.length() < bestVec.length()) {
                    bestVec = shotVec;
                }
            }
        }
        if(bestVec != null) return bestVec;
        else return baseVec;
    }

    Player computer;
    Player player;
    public void add(Sprite sprite) {
        super.add(sprite);
        if(sprite instanceof Shot) {
            Shot shot = (Shot) sprite;
            shots.add(shot);
            this.add(shot.getShadow());
            this.moveToBottom(shot.getShadow());
            this.moveUp(shot.getShadow());
        }
        else if(sprite instanceof Target) {
            Target target = (Target) sprite;
            targets.add(target);
            this.add(target.getShadow());
            this.moveToBottom(target.getShadow());
            this.moveUp(target.getShadow());
        }
        else if(sprite instanceof ComputerPlayer) {
            computer = (ComputerPlayer)sprite;
            this.add(computer.getShadow());
            this.moveToBottom(computer.getShadow());
            this.moveUp(computer.getShadow());
        }
        else if(sprite instanceof Player) {
            player = (Player)sprite;
            this.add(player.getShadow());
            this.moveToBottom(player.getShadow());
            this.moveUp(player.getShadow());
        }
        else if(sprite instanceof Creature) {
            Creature c = (Creature) sprite;
            creatures.add(c);
            this.add(c.getShadow());
            this.moveToBottom(c.getShadow());
            this.moveUp(c.getShadow());
        }
        moveToTop(scores);
    }


    public void remove(Sprite sprite) {
        super.remove(sprite);
        if(sprite instanceof Shot) {
            Shot shot = (Shot) sprite;
            shots.remove(shot);
            this.remove(shot.getShadow());
            this.getWorld().destroyBody(shot.getBody());
        }
        else if(sprite instanceof Target) {
            Target target = (Target) sprite;
            targets.remove(target);
            this.remove(target.getShadow());
            this.getWorld().destroyBody(target.getBody());
        }
        else if(sprite instanceof Creature) {
            Creature creature = (Creature) sprite;
            this.remove(creature.getShadow());
            creatures.remove(creature);
            for(int i=0;i<creature.getNumSprites();i++) {
                this.getWorld().destroyBody(((BodySprite)creature.get(i)).getBody());
            }
        }
    }

    @Override
    public void update(int elapsedTime) {
        super.update(elapsedTime);

        // Roll the shots into the shooter containers
        for (Shot shot : shots) {
            if(shot.x.get() < this.width.get() / 2) {
                shot.push(-1,0,5);
            } else {
                shot.push(1,0,5);
            }

            if(shot.x.get() < 100 || shot.x.get() > 700) {
                if(shot.y.get() < this.height.get() / 2) {
                    shot.push(0,-1,5);
                } else {
                    shot.push(0,1,5);
                }
            }
        }

        // If a target enters a side, respawn it in the middle
        ArrayList<Target> targetsToRespawn = new ArrayList<Target>();
        for(Target target : targets) {
            if(target.x.get() < player.x.get() || target.x.get() > computer.x.get()) {
                if(target.x.get() < player.x.get()) {
                    ding.play(new Fixed(1),new Fixed(-0.75)); //DING!
                    scoreAwayVal.setText(Integer.toString(++scoreComputer));
                }
                else {
                    ding.play(new Fixed(1),new Fixed(0.75)); //DING!
                    scoreHomeVal.setText(Integer.toString(++scorePlayer));
                }
//                this.add(new Target(BodyUtils.createBox(this.getWorld(),0,0,4,4),this));
                targetsToRespawn.add(target);
            }
        }
        for(Target target : targetsToRespawn) {
            this.remove(target);
            spawnTarget(0,CoreMath.rand(-10,10));
        }


        //TODO: clean up the copy/paste
        // If a creature enters a side, respawn it in the middle
        ArrayList<Creature> creaturesToRespawn = new ArrayList<Creature>();
        for(Creature creature : creatures) {
            double cx = creature.get(0).x.get();
            if(cx < player.x.get() || cx > computer.x.get()) {
                if(cx < player.x.get()) {
                    ding.play(new Fixed(1),new Fixed(-0.75)); //DING!
                    scoreAwayVal.setText(Integer.toString(++scoreComputer));
                }
                else {
                    ding.play(new Fixed(1),new Fixed(0.75)); //DING!
                    scoreHomeVal.setText(Integer.toString(++scorePlayer));
                }
//                this.add(new Target(BodyUtils.createBox(this.getWorld(),0,0,4,4),this));
                creaturesToRespawn.add(creature);
            }
        }
        for(Creature creature: creaturesToRespawn) {
            this.remove(creature);
            spawnCreature(0,(float)CoreMath.rand(-10.0,10.0));
        }

    }


    private void spawnCreature(float x, float y) {
        Sprite s = new FilledSprite(x,y,200,25, Colors.GREEN);
        s.setAnchor(Sprite.CENTER);
        Creature c = null;
        boolean intersect;
        int counter = 0;
        do {
            if(++counter > 20) break;
            y += 1;
            if(y > 10) y -= 20;
            intersect = intersectsTarget(x, y, s);
//            if(intersect) System.out.println("intersect creature!,counter="+counter);
        } while (intersect);
        c = new Creature(this,-y*2);
        add(c);
//        add(s);
        c.alpha.animate(0,255,1000);
//        s.alpha.animate(255,0,1000);
    }

    private boolean intersectsTarget(float x, float y, Sprite s) {
        s.setLocation(width.get()/2 + x*getScaling(),height.get()/2 - y*getScaling());
        for (Creature creature : creatures) {
                if (creature.intersects(s)) {
                    return true;
                }
        }
        for (Target target : targets) {
            if (target.intersects(s)) return true;
        }
        return false;
    }

    public void spawnTarget(float x, float y) {
        float size = (float) CoreMath.rand(1.0,2.5);
        Sprite s = new FilledSprite(x,y,size*getScaling(), size*getScaling(),Colors.RED);
        s.setAnchor(Sprite.CENTER);
        int counter = 0;
        boolean intersect;
        do {
            y += 1;
            if(y > 10) y -= 20;
            intersect = intersectsTarget(x, y, s);
//            if(intersect) System.out.println("intersect target!,counter="+counter);
            if(++counter > 20) break;
        } while (intersect);
        Target targetAdd = new Target(BodyUtils.createRoundRectangle(this.getWorld(), x,y,size, size,size/4, Material.RANDOM()), this,2);
        this.add(targetAdd);
//        add(s);
        targetAdd.alpha.animate(0,255,1000);
//        s.alpha.animate(255,0,1000);
    }

    public Shot getNearestShot(double x, double y, double dist) {
        Vec2 baseVec = new Vec2((float)x,(float)y);
        for (Shot shot: shots) {
            Vec2 shotVec = new Vec2((float)shot.x.get(),(float)shot.y.get());
            shotVec.subLocal(baseVec);
            if(shotVec.length() < dist) {
                return shot;
            }
        }
        return null;
    }

    public Shot getPooledShot(double x, double y, double width, double height) {
        Vec2 baseVec = new Vec2((float)x,(float)y);
        for (Shot shot: shots) {
            double shotX = shot.x.get();
            double shotY = shot.y.get();
            if (shotX <= x + width && shotX >= x &&
                    shotY <= y + height && shotY >= y) {
                return shot;
            }
        }
        return null;
    }

}
